commonlibsse_ng\re\e/
ExtraDataList.rs1use core::ffi::CStr;
2use core::ptr::{self, NonNull};
3
4use crate::re::BGSEncounterZone::BGSEncounterZone;
5use crate::re::BSAtomic::BSReadWriteLock;
6use crate::re::BSExtraData::{
7 BSExtraData, BSExtraDataIter, BSExtraDataIterMut, DerivedBSExtraData, downcast_as,
8};
9use crate::re::BSPointerHandle::ObjectRefHandle;
10use crate::re::ExtraAshPileRef::ExtraAshPileRef;
11use crate::re::ExtraCount::ExtraCount;
12use crate::re::ExtraDataType::ExtraDataType;
13use crate::re::ExtraEncounterZone::ExtraEncounterZone;
14use crate::re::ExtraHealth::ExtraHealth;
15use crate::re::ExtraReferenceHandle::ExtraReferenceHandle;
16use crate::re::ExtraTextDisplayData::ExtraTextDisplayData;
17use crate::re::TESBoundObject::TESBoundObject;
18use crate::re::TESBox::TESBox;
19use crate::rel::relocation::PhantomMember;
20
21#[repr(C)]
22#[derive(Debug, Clone)]
23pub struct ExtraDataList {
24 extra_data: BaseExtraList,
25 lock: PhantomMember<BSReadWriteLock, 0x10, 0x18>,
27
28 pad: usize,
32}
33const _: () = assert!(core::mem::size_of::<ExtraDataList>() == 0x8);
34
35unsafe impl std_fork::zeroable::Zeroable for ExtraDataList {}
36
37impl ExtraDataList {
38 #[inline]
40 pub fn has_type(&self, type_: ExtraDataType) -> bool {
41 let _lock = self.lock.get();
42 match self.extra_data.presence.get() {
43 Ok(presence) => {
44 unsafe { presence.as_ref() }.is_some_and(|presence| presence.has_type(type_.bits()))
45 }
46 Err(_err) => {
47 #[cfg(feature = "tracing")]
48 tracing::error!("Error getting presence address: {_err}");
49 false
50 }
51 }
52 }
53
54 pub fn remove(&mut self, type_: ExtraDataType, to_remove: *mut BSExtraData) -> bool {
58 let _lock = match self.lock.get_mut() {
59 Ok(lock) => lock.write(),
60 Err(_err) => return false,
61 };
62
63 if to_remove.is_null() {
64 return false;
65 }
66
67 let mut removed = false;
68
69 let data = match self.extra_data.data.0.get_mut() {
70 Ok(data) => data,
71 Err(_) => return false,
72 };
73 if (*data) == to_remove {
74 *data = (unsafe { &**data }).next;
75 removed = true;
76 } else {
77 let mut iter = self.extra_data.data.iter();
78 while iter.next().is_some() {
79 if iter.remove_current().is_some() {
80 removed = true;
81 };
82 }
83 }
84
85 if removed {
86 if let Ok(presence) = self.extra_data.presence.get_mut() {
87 match unsafe { presence.as_mut() } {
88 Some(presence) => presence.mark_type(type_.bits(), true),
89 None => return false,
90 };
91 };
92 }
93
94 removed
95 }
96
97 pub fn remove_by_type(&mut self, type_: ExtraDataType) -> bool {
99 let _lock = match self.lock.get_mut() {
100 Ok(lock) => lock.write(),
101 Err(_err) => return false,
102 };
103
104 let mut removed = false;
105
106 let mut iter = self.extra_data.data.iter_mut();
107 while let Some(data) = iter.next() {
108 if (unsafe { &*data }).get_type() == type_ {
109 iter.remove_current();
110 }
111 removed = true;
112 }
113
114 if removed {
115 if let Ok(presence) = self.extra_data.presence.get_mut() {
116 match unsafe { presence.as_mut() } {
117 Some(presence) => presence.mark_type(type_.bits(), true),
118 None => return false,
119 };
120 };
121 }
122
123 removed
124 }
125
126 #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 12176, ae_id = 12315)]
128 pub fn add(&mut self, item: *mut BSExtraData) -> *mut BSExtraData {}
129
130 pub fn get_ash_pile_ref(&mut self) -> ObjectRefHandle {
131 let ash_ref = self.get_by_type_as::<ExtraAshPileRef>();
132 ash_ref
133 .map(|ash_ref| unsafe { ash_ref.as_ref() })
134 .map_or_else(ObjectRefHandle::default, |ash_ref| ash_ref.ashPileRef.clone())
135 }
136
137 pub fn get_count(&self) -> i32 {
138 let x_count = self.get_by_type_as::<ExtraCount>();
139 x_count.map_or(1, |x_count| unsafe { x_count.as_ref().count as i32 })
140 }
141
142 #[allow(clippy::not_unsafe_ptr_arg_deref)]
143 pub fn get_display_name<'a>(&mut self, base_object: &'a TESBoundObject) -> Option<&'a CStr> {
144 let health = self
145 .get_by_type_as::<ExtraHealth>()
146 .map_or(1.0, |x_health| unsafe { x_health.as_ref().health });
147
148 let df_health = if health <= 1.0 { (1.0 - health) < 0.001 } else { (health - 1.0) < 0.001 };
149
150 let mut x_text = self.get_extra_text_display_data();
151 if x_text.is_none() && !df_health {
152 let x_text_ptr = TESBox::into_non_null(TESBox::new(ExtraTextDisplayData::new()));
153 x_text.get_or_insert(x_text_ptr);
154 self.add(x_text_ptr.as_ptr().cast());
155 }
156
157 let c_char_ptr =
158 unsafe { x_text?.as_mut().get_display_name(base_object, health).as_ref() }?;
159 unsafe { Some(CStr::from_ptr(c_char_ptr)) }
160 }
161
162 #[inline]
163 pub fn get_encounter_zone(&self) -> Option<NonNull<BGSEncounterZone>> {
164 self.get_by_type_as::<ExtraEncounterZone>()
165 .and_then(|x_ref| unsafe { NonNull::new(x_ref.as_ref().zone) })
166 }
167
168 pub fn get_extra_text_display_data(&self) -> Option<NonNull<ExtraTextDisplayData>> {
169 let tes_ref = self
170 .get_by_type_as::<ExtraReferenceHandle>()
171 .map(|x_ref| unsafe { x_ref.as_ref().get_original_reference() })?;
172
173 tes_ref.as_ref().map_or_else(
174 || self.get_by_type_as::<ExtraTextDisplayData>(),
175 |tes_ref| {
176 if tes_ref.__base.is_deleted() {
177 tes_ref
178 .extraList
179 .get_by_type_as::<ExtraTextDisplayData>()
180 .map_or_else(|| self.get_by_type_as::<ExtraTextDisplayData>(), Some)
181 } else {
182 self.get_by_type_as::<ExtraTextDisplayData>()
183 }
184 },
185 )
186 }
187
188 #[inline]
191 pub fn get_by_type(&self, type_: ExtraDataType) -> Option<*mut BSExtraData> {
192 let _lock = self.lock.get();
193 self.extra_data
194 .data
195 .iter()
196 .find(|data| unsafe { data.as_ref() }.is_some_and(|data| data.get_type() == type_))
197 }
198
199 #[inline]
205 pub fn get_by_type_as<T>(&self) -> Option<NonNull<T>>
206 where
207 T: DerivedBSExtraData,
208 {
209 downcast_as(self.get_by_type(T::get_extra_data_type())?)
210 }
211}
212
213#[repr(C)]
214#[derive(Debug, Clone)]
215pub struct BaseExtraList {
216 data: Data,
219 presence: PhantomMember<*mut PresenceBitfield, 0x8, 0x10>,
220}
221const _: () = assert!(core::mem::size_of::<BaseExtraList>() == 0x0);
222
223#[derive(Debug, Clone)]
225pub struct Data(pub PhantomMember<*mut BSExtraData, 0x0, 0x8>);
226
227impl Data {
228 #[inline]
231 pub fn iter(&self) -> BSExtraDataIter<'_> {
232 BSExtraDataIter::new(self.0.get().copied().unwrap_or(ptr::null_mut()))
233 }
234
235 #[inline]
238 pub fn iter_mut(&mut self) -> BSExtraDataIterMut<'_> {
239 BSExtraDataIterMut::new(self.0.get().copied().unwrap_or(ptr::null_mut()))
240 }
241}
242
243impl<'a> IntoIterator for &'a Data {
244 type Item = *mut BSExtraData;
245 type IntoIter = BSExtraDataIter<'a>;
246
247 fn into_iter(self) -> Self::IntoIter {
248 self.iter()
249 }
250}
251
252impl<'a> IntoIterator for &'a mut Data {
254 type Item = *mut BSExtraData;
255 type IntoIter = BSExtraDataIterMut<'a>;
256
257 fn into_iter(self) -> Self::IntoIter {
258 self.iter_mut()
259 }
260}
261
262#[repr(C)]
263#[derive(Debug, Clone, Default)]
264pub struct PresenceBitfield {
265 pub bits: [u8; 0x18], }
267const _: () = assert!(core::mem::size_of::<PresenceBitfield>() == 0x18);
268
269impl PresenceBitfield {
270 pub const fn has_type(&self, type_: u32) -> bool {
271 let index = (type_ >> 3) as usize;
272 if index >= self.bits.len() {
273 return false;
274 }
275 let bit_mask = 1 << (type_ % 8);
276 (self.bits[index] & bit_mask) != 0
277 }
278
279 pub const fn mark_type(&mut self, type_: u32, cleared: bool) {
280 let index = (type_ >> 3) as usize;
281 if index >= self.bits.len() {
282 return;
283 }
284 let bit_mask = 1 << (type_ % 8);
285 let flag = &mut self.bits[index];
286 if cleared {
287 *flag &= !bit_mask;
288 } else {
289 *flag |= bit_mask;
290 }
291 }
292}